<?php
/*******************************************************************************

    FinalsClub.org is a platform for students and faculty to collaborate
    and share their knowledge with the world.

    Copyright (C) 2009  Andrew Magliozzi
 
    This file is a part of FinalsClub.org.

    FinalsClub.org is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
    To contact FinalsClub.org, send an email to info@finalsclub.org, or
    send mail to Finals Club, c/o Veritas Tutors, 1132 Massachusetts Avenue,
    Cambridge, MA 02138.

*******************************************************************************/

class Search {

	const NOTE = 1;
	const COURSE = 2;
	const GROUP = 3;
	const USER = 4;
	const FILE = 5;
	const BOOK = 6;
	const ANNOTATION = 7;

	static $CACHELIFETIME = 3600; // secs

	static $cache = NULL;

	/**
	 *  Get Lucene Index
	 *
	 * @param type the type of Lucene index (SEARCH::NOTE etc)
	 * @return the actual lucene index for that type
	 */
	static public function getLuceneIndex($type) {
  		ProjectConfiguration::registerZend();
 
  		if (file_exists($index = self::getLuceneIndexFile($type))) {
    			return Zend_Search_Lucene::open($index);
  		} else {
    			return Zend_Search_Lucene::create($index);
  		}
	}

	/**
	 * Get the Lucene Index File
	 *
	 * @param type the type of Lucene index (SEARCH::NOTE etc)
	 * @return the filepath of the specified lucene index type
	 */
	static public function getLuceneIndexFile($type) {
		switch ($type) {
			case Search::NOTE: return dirname(__FILE__).'/../../data/search.index/notes';
			case Search::COURSE: return dirname(__FILE__).'/../../data/search.index/courses';
			case Search::GROUP: return dirname(__FILE__).'/../../data/search.index/groups';
			case Search::USER: return dirname(__FILE__).'/../../data/search.index/users';
			case Search::FILE: return dirname(__FILE__).'/../../data/search.index/files';
			case Search::BOOK: return dirname(__FILE__).'/../../data/search.index/books';
			case Search::ANNOTATION: return dirname(__FILE__).'/../../data/search.index/annotations';
			default: return dirname(__FILE__).'/../../data/search.index/def';
		}
	}

	/**
	 * Do a query via lucene with the specific lucene index type
 	 *
	 * @param type the type of Lucene index (SEARCH::NOTE etc)
	 * @param query the query string
	 * @return array of hits
	 */
	static public function getForLuceneQuery($type,$query) {
  		$hits = self::getLuceneIndex($type)->find($query);
 		return $hits;
	}
	
	/**
	 * Cache search results
 	 *
	 * @param results array of results
	 * @return the id in cache
	 */
	static public function cacheResults($results) {
		$newid = self::getNewId();
		return self::cacheResultsWithId('sres'.$newid,$results);
	}

	static public function getNewId() {
		$cacheref = self::getSearchCache();
		if($cacheref->has('searchrefid')) {
			$id = $cacheref->get('searchrefid');
		} else {
			$id = 0;
		}
		$newid = $id + 1;
		$cacheref->set('searchrefid',$newid);
		return $id;
	}

	/**
	 * Cache search results
 	 *
	 * @param id ref id in cache
	 * @param results array of results
	 * @return the id in cache
	 */
	static public function cacheResultsWithId($id, $results) {
    		$cacheref = self::getSearchCache();
		if(!$cacheref) {
			return NULL;
		}  

		// turns out Zend_Search_Lucene results cause all sorts of issues if we serialize
		// the results directly.  so instead we serialize and cache the resultant ids  - mnakamura
		$resultSet = array();
		foreach ($results as $hit) {
    			$resultSet[] = array($hit->id, $hit->score);
		}
    		$cacheref->set($id, serialize($resultSet),Search::$CACHELIFETIME); 
		return $id;
	}

	/**
	 * get cached search results
 	 *
	 * @param id the id ref in cache
	 * @return the results or NULL (note the results are an array of result ids and their respective scores)
	 */
	static public function retrieveCachedResults($id) {
		$cacheref = self::getSearchCache();
		if(!$cacheref) {
			return NULL;
		}
    		if ($cacheref->has($id)) {
      			$results = unserialize($cacheref->get($id));
     			return $results;
    		}
		return NULL;
	}

	/**
	 * get cached search result documents (hits)
 	 *
	 * @param id the id ref in cache
	 * @param type the type of search index to use
	 * @param start the index point in the array to start from
    	 * @param num the number of results to return (if it exceeds the actual number of elements left it will only return that number)
	 * @return the result documents or NULL 
	 */
	static public function retrieveCachedResultsReturnDocs($id, $type, $start, $num) {
		if($results = self::retrieveCachedResults($id)) {
			$resultdocs = array();
			$size = count($results) - 1;
			$index = self::getLuceneIndex($type);
			for ($count = $start; $count < $start+$num; $count++) {
				if($count > $size) {
					break;				
				}
				$resultdocs[] = $index->getDocument($results[$count][0]);
			}	
			return $resultdocs;
		}
		return NULL;
	}

	/**
	 * get ALL cached search result documents (hits)
 	 *
	 * @param id the id ref in cache
	 * @param type the type of search index to use
	 * @return the result documents or NULL 
	 */
	static public function retrieveCachedResultsReturnDocsAll($id, $type) {
		if($results = self::retrieveCachedResults($id)) {
			$resultdocs = array();
			$index = self::getLuceneIndex($type);
			foreach ($results as $result) {
				$resultdocs[] = $index->getDocument($result[0]);
			}	
			return $resultdocs;
		}
		return NULL;
	}
	
	/**
	 * get the search cache ref
 	 *
	 * @return the cache ref
	 */
	static public function getSearchCache() {
		if (!self::$cache instanceof sfFileCache) {
      			$file_cache_dir = array('cache_dir' => dirname(__FILE__).'/../../cache/search_cache');
      			self::$cache = new sfFileCache($file_cache_dir);
    		}
    		return self::$cache;
	}
}
